package com.jkdz.spyke.flexUI.helpers {
	import mx.core.UIComponent;

	import flash.external.ExternalInterface;
	import flash.utils.ByteArray;
	import flash.utils.Dictionary;
	import flash.utils.setTimeout;
	
	public class FileDropManager {
		
		private static var dropTargets:Dictionary;
		private static var chunkWaiters:Object;
		
		public static function initDropping():void {
			dropTargets = new Dictionary();
			chunkWaiters = new Object();
			
			ExternalInterface.addCallback("fileDropped", function(atX:int, atY:int, id:int, name:String, length:int):void {
				var f:FileDropListener = findDropTarget(atX, atY);
				if (f != null) {
					f.fileDropped(new FileDropReference(id, name, length));
				}
				clearFileOverMarkers();
				setFileNearMarkers(false);
			});
			
			ExternalInterface.addCallback("fileOver", function(overX:int, overY:int):Boolean {
				setFileNearMarkers(true);
				clearFileOverMarkers();
				var f:FileDropListener = findDropTarget(overX, overY);
				if (f != null) {
					f.setFileOver(true);
					return true;
				} else {
					return false;
				}
			});
			
			ExternalInterface.addCallback("fileOut", function():void {
				clearFileOverMarkers();
				setFileNearMarkers(false);
			});
			
			ExternalInterface.addCallback("chunkLoaded", function(chunkID:String, chunk:*):void {
				var waiter:Function = chunkWaiters[chunkID];
				delete chunkWaiters[chunkID];
				setTimeout(function():void {
					waiter(chunk);
				}, 1);
			});
		}
		
		private static function clearFileOverMarkers():void {
			for (var element:* in dropTargets) {
				dropTargets[element].setFileOver(false);
			}
		}
		
		private static function setFileNearMarkers(near:Boolean):void {
			for (var element:* in dropTargets) {
				dropTargets[element].setFileNear(near);
			}
		}
		
		public static function getChunk(fileID:int, from:int, length:int, onRead:Function):void {
			var chunkID:String = "file"+fileID+"chunk"+from;
			chunkWaiters[chunkID] = function(chunk:String):void {
				var ret:ByteArray = new ByteArray();
				
				for (var i:int = 0; i<chunk.length; ++i) {
					ret.writeByte(chunk.charCodeAt(i));
				}
				
				ret.position = 0;
				
				onRead(ret);
			};
			ExternalInterface.call("asyncGetFileChunk", fileID, from, length, chunkID);
		}
		
		private static function findDropTarget(atX:int, atY:int):FileDropListener {
			var tti:UIComponent = null;
			for (var i:* in dropTargets) {
				var ti:UIComponent = i;
				if (ti.hitTestPoint(atX, atY)) {
					if (tti == null) {
						tti = ti;
					} else {
						if (isHigherThan(ti, tti)) {
							tti = ti;
						}
					}
				}
			}
			return dropTargets[tti];
		}

		private static function isHigherThan(a:UIComponent, b:UIComponent):Boolean {
			var aps:Vector.<UIComponent> = getOwners(a);
			var bps:Vector.<UIComponent> = getOwners(b);
			
			var tap:UIComponent = a;
			for each (var ap:UIComponent in aps) {
				var tbp:UIComponent = b;
				for each (var bp:UIComponent in bps) {
					if (ap == bp) {
						return isHigherInSameContainerThan(tap, tbp);
					}
					tbp = bp;
				}
				tap = ap;
			}
			
			return false;
		}
		
		private static function isHigherInSameContainerThan(a:UIComponent, b:UIComponent):Boolean {
			if (a.depth == b.depth) {
				return a.parent.getChildIndex(a) > b.parent.getChildIndex(b);
			} else {
				return a.depth > b.depth;
			}
		}
		
		private static function getOwners(e:UIComponent):Vector.<UIComponent> {
			var ret:Vector.<UIComponent> = new Vector.<UIComponent>();
			while (e.owner != null && e.owner is UIComponent && e.owner != e) {
				e = (UIComponent)(e.owner);
				ret.push(e);
			}
			ret.push(null);
			return ret;
		}
		
		public static function addFileDropListener(element:UIComponent, listener:FileDropListener):void {
			dropTargets[element] = listener;
		}
		
		public static function removeDropListener(element:UIComponent):void {
			delete dropTargets[element];
		}
	}
}